package com.framework.foundation.util.sqlxml;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import com.framework.foundation.util.FreemarkerKit;
import com.framework.foundation.util.XMLConverUtil;
import com.google.common.collect.Maps;
import com.jfinal.log.Log;

public class SqlKit {

	protected static final Log LOG = Log.getLog(SqlKit.class);

	private static Map<String, String> sqlMap;

	static void clearSqlMap() {
		sqlMap.clear();
	}

	static void init() {
		init("");
	}

	static void init(String... paths) {
		sqlMap = new HashMap<String, String>();

		for (String path : paths) {
			if (path.startsWith("/")) {
				LOG.error("SqlInXmlPlugin 路径不正确, 不能以\"/\"开头");
				return;
			}

			Enumeration<URL> baseURLs = null;
			try {
				baseURLs = SqlKit.class.getClassLoader().getResources(path);
				URL url = null;
				while (baseURLs.hasMoreElements()) {
					url = baseURLs.nextElement();
					loadFilePath(url);
				}

			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		LOG.debug("sqlMap" + sqlMap);
	}

	private static void loadFilePath(URL baseURL) {
		if (baseURL != null) {
			String protocol = baseURL.getProtocol();
			String basePath = baseURL.getFile();
			if ("jar".equals(protocol)) {
				String[] pathurls = basePath.split("!/");
				// 获取jar
				try {
					loadJarFileList(URLDecoder.decode(pathurls[0].replace("file:", ""), "UTF-8"), pathurls[1]);
				} catch (IOException e) {
					e.printStackTrace();
				}
			} else {
				// 加载sql文件
				loadFileList(basePath);
			}
		}
	}

	public static void loadFileList(String strPath) {
		List<File> files = new ArrayList<File>();
		File dir = new File(strPath);
		File[] dirs = dir.listFiles(new FileFilter() {
			@Override
			public boolean accept(File pathname) {
				if (pathname.getName().endsWith("sql") || pathname.getName().endsWith("sql.xml")) {
					return true;
				}
				return false;
			}
		});

		if (dirs == null) {
			return;
		}
		for (File dir2 : dirs) {
			if (dir2.isDirectory()) {
				loadFileList(dir2.getAbsolutePath());
			} else {
				if (dir2.getName().endsWith("sql.xml")) {
					files.add(dir2);
				}
			}
		}
		// 加载sql文件
		loadFiles(files);
	}

	/**
	 * find jar file
	 *
	 * @param filePath 文件路径
	 * @param packageName 包名
	 * @return list
	 * @throws java.io.IOException 文件读取异常
	 */
	private static void loadJarFileList(String filePath, String packageName) throws IOException {
		Map<String, InputStream> sqlFiles = Maps.newHashMap();
		JarFile localJarFile = new JarFile(new File(filePath));
		sqlFiles = findInJar(localJarFile, packageName);
		// 加载sql文件
		loadStreamFiles(sqlFiles);
		localJarFile.close();
	}

	/**
	 * 从jar里加载文件
	 *
	 * @param localJarFile 文件路径
	 * @param packageName 包名
	 * @return
	 */
	private static Map<String, InputStream> findInJar(JarFile localJarFile, String packageName) {
		Map<String, InputStream> sqlFiles = Maps.newHashMap();
		Enumeration<JarEntry> entries = localJarFile.entries();
		while (entries.hasMoreElements()) {
			JarEntry jarEntry = entries.nextElement();
			String entryName = jarEntry.getName();
			if (!jarEntry.isDirectory() && ((packageName == null) || entryName.startsWith(packageName))
					&& entryName.endsWith("sql.xml")) {
				sqlFiles.put(entryName.substring(entryName.lastIndexOf("/") + 1),
						SqlKit.class.getResourceAsStream(File.separator + entryName));
			}
		}
		return sqlFiles;
	}

	/**
	 * 加载xml文件
	 *
	 * @param files
	 */
	private static void loadFiles(List<File> files) {
		try {
			for (File xmlfile : files) {
				FileInputStream fis = new FileInputStream(xmlfile);
				SqlRoot root = XMLConverUtil.convertToObject(SqlRoot.class, fis);
				for (SqlGroup sqlGroup : root.sqlGroups) {

					sql(xmlfile.getName(), sqlGroup);
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	private static void loadStreamFiles(Map<String, InputStream> streams) {
		try {
			for (String filename : streams.keySet()) {
				FileInputStream fis = new FileInputStream(new File(filename));
				SqlRoot root = XMLConverUtil.convertToObject(SqlRoot.class, fis);
				for (SqlGroup sqlGroup : root.sqlGroups) {

					sql(filename, sqlGroup);
				}
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	private static void sql(String filename, SqlGroup sqlGroup) {
		String name = sqlGroup.name;
		if ((name == null) || name.trim().equals("")) {
			name = filename;
		}
		for (SqlItem sqlItem : sqlGroup.sqlItems) {
			sqlMap.put(name + "." + sqlItem.id, sqlItem.value);
		}
	}

	/**
	 * 获取SQL,固定SQL
	 * 
	 * @param groupNameAndsqlId
	 * @return
	 * @Author: Brin
	 * @Date: 2015年10月20日 下午1:55:36
	 */
	public static String getSql(String groupNameAndsqlId) {
		if (sqlMap == null) {
			throw new NullPointerException("SqlInXmlPlugin not start");
		}
		return sqlMap.get(groupNameAndsqlId);
	}

	/**
	 * 获取SQL，动态SQL
	 * 说明: 需要进一步解析的动态sql语句
	 * 说明: 固定sql最好不要配置到文件中,配置文件主要配置动态的sql语句,固定的语句直接手写即可方便修改,毕竟固定语句多了配置文件管理起来反而麻烦
	 * 
	 * @param groupNameAndsqlId groupName + "." + sqlId, eg:user.findAll
	 * @return 使用freemarker解析sql
	 * @Author: Brin
	 * @Date: 2015年10月16日 上午11:15:10
	 */
	public static String getSqlByFreemarker(String groupNameAndsqlId) {
		return getSqlByFreemarker(groupNameAndsqlId, null);
	}

	/**
	 * 获取SQL，动态SQL
	 * 
	 * @param groupNameAndsqlId groupName + "." + sqlId, eg:user.findAll
	 * @param param sql中的参数
	 * @return 使用freemarker解析sql
	 * @Author: Brin
	 * @Date: 2015年10月16日 上午11:15:10
	 */
	public static String getSqlByFreemarker(String groupNameAndsqlId, Map<String, Object> param) {
		String sqlTemplete = getSql(groupNameAndsqlId);
		// 使用freemark解析sql模板
		String sql = null;
		try {
			sql = FreemarkerKit.render(sqlTemplete, param);
		} catch (Exception e) {
			LOG.error("freemarker解析sql模板出错:" + e.getMessage());
			return null;
		}
		return sql.trim().replaceAll("[\\s]{2,}", " ");
	}
}
